#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <string.h>
#include <errno.h>

//这个里面涉及的信号量以及共享内存的操作都是基于最新的posix标准做的
/*
信号灯操作：
    1、创建命名信号灯
    sem_t *sem = sem_open(name, O_CREAT, 0666, value);
    2、信号灯的P--操作，消费资源
    sem_wait(&sem_data);
    3、信号灯的V++操作，生产资源
    sem_post(&sem_data);
    4、关闭信号量
    sem_close(&sem_data);
共享内存操作：
    1、创建共享内存对象，这个fd用于给mmap传参，用于映射的文件对象
    int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    2、设置共享内存大小，如果是普通文件，相当于设置普通文件的大小
    ftruncate(shm_fd, sizeof(shared_data))
    3、创建共享内存映射
    shared_data *data = mmap(NULL, sizeof(shared_data), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    4、映射完整后就可以直接诶, 关闭文件描述符（不影响映射）
    close(shm_fd);
    5、操作完之后就可以解除共享内存映射
    munmap(data, sizeof(shared_data));
*/

//编译：gcc -o ipc ipc.c -lrt -lpthread

#define SHM_NAME "/my_shared_memory"  // 共享内存名称
#define SEM_EMPTY_NAME "/sem_empty"   // 表示空位的信号灯
#define SEM_FULL_NAME "/sem_full"     // 表示满位的信号灯
#define SHM_SIZE 1024                 // 共享内存大小
#define MAX_ITEMS 10                  // 缓冲区最大项目数

// 共享内存数据结构
typedef struct {
    int buffer[MAX_ITEMS];  // 循环缓冲区
    int in;                 // 生产者插入位置
    int out;                // 消费者取出位置
} shared_data;

// 错误处理函数
void handle_error(const char *msg) {
    perror(msg);
    exit(EXIT_FAILURE);
}

// 创建或获取共享内存
shared_data *setup_shared_memory() {
    // 创建共享内存对象
    int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    if (shm_fd == -1) {
        handle_error("shm_open failed");
    }
    
    // 设置共享内存大小
    if (ftruncate(shm_fd, sizeof(shared_data))) {
        handle_error("ftruncate failed");
    }
    
    // 映射共享内存
    shared_data *data = mmap(NULL, sizeof(shared_data), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (data == MAP_FAILED) {
        handle_error("mmap failed");
    }
    
    // 初始化共享内存数据结构, 和这个操作在谁第一次调用的时候去执行处理
    //memset(data, 0, sizeof(shared_data));
    
    // 关闭文件描述符（不影响映射）
    close(shm_fd);
    
    return data;
}

// 创建或获取信号灯
sem_t *setup_semaphore(const char *name, int value) {
    // 创建命名信号灯
    sem_t *sem = sem_open(name, O_CREAT, 0666, value);
    if (sem == SEM_FAILED) {
        handle_error("sem_open failed");
    }
    return sem;
}

// 清理资源
void cleanup_resources(shared_data *data, sem_t *empty, sem_t *full) {
    // 解除共享内存映射
    if (munmap(data, sizeof(shared_data))) {
        perror("munmap failed");
    }
    
    // 关闭信号灯
    if (sem_close(empty)) {
        perror("sem_close(empty) failed");
    }
    if (sem_close(full)) {
        perror("sem_close(full) failed");
    }
}

// 生产者进程
void producer_process() {
    printf("生产者进程启动 (PID: %d)\n", getpid());
    int i;
    // 设置共享内存和信号灯
    shared_data *data = setup_shared_memory();
    sem_t *empty = setup_semaphore(SEM_EMPTY_NAME, MAX_ITEMS);  //设置空位的初始信号灯的值为10
    sem_t *full = setup_semaphore(SEM_FULL_NAME, 0);            //满位的信号灯值为 0

    // 首次运行时初始化缓冲区
    static int initialized = 0;
    if (!initialized) {
        memset(data, 0, sizeof(shared_data));
        initialized = 1;
    }

    // 生产10个项目
    for (i = 1; i <= 10; i++) {
        sleep(1); // 模拟生产过程耗时
        
        // P-- 操作等待空位 (消费空信号灯的值)  初始值                   等待sem_post(empty)唤醒
        if (sem_wait(empty)) {
            handle_error("sem_wait(empty) failed");
        }
        
        // 生产项目
        data->buffer[data->in] = i;  // buf[i] = i + 1; 
        printf("生产者: 生产项目 %d 到位置 %d\n", i, data->in);
        data->in = (data->in + 1) % MAX_ITEMS;   //0 ~ 9
        
        // 通知有新项目 (增加满信号灯)
        if (sem_post(full)) {
            handle_error("sem_post(full) failed");
        }
    }
    
    printf("生产者完成\n");
    // 清理资源
    cleanup_resources(data, empty, full);
}

// 消费者进程
void consumer_process() {
    printf("消费者进程启动 (PID: %d)\n", getpid());

	int i;
    // 设置共享内存和信号灯
    shared_data *data = setup_shared_memory();
    sem_t *empty = setup_semaphore(SEM_EMPTY_NAME, MAX_ITEMS);
    sem_t *full = setup_semaphore(SEM_FULL_NAME, 0);
    
    // 消费10个项目
    for (i = 0; i < 10; i++) {
        // 等待有项目可消费 (减少满信号灯) 等待 sem_post(full) 唤醒
        if (sem_wait(full)) {
            handle_error("sem_wait(full) failed");
        }
        
        // 消费项目
        int item = data->buffer[data->out];
        printf("消费者: 消费项目 %d 从位置 %d\n", item, data->out);
        data->out = (data->out + 1) % MAX_ITEMS;
        
        // 通知有空位 (增加空信号灯)
        if (sem_post(empty)) {
            handle_error("sem_post(empty) failed");
        }
        
        sleep(2); // 模拟消费过程耗时
    }
    
    printf("消费者完成\n");
    
    // 清理资源
    cleanup_resources(data, empty, full);
}

// 清理所有系统资源
void global_cleanup() {
    // 删除共享内存对象
    if (shm_unlink(SHM_NAME)) {
        perror("shm_unlink failed");
    }
    
    // 删除信号灯
    if (sem_unlink(SEM_EMPTY_NAME)) {
        perror("sem_unlink(SEM_EMPTY_NAME) failed");
    }
    if (sem_unlink(SEM_FULL_NAME)) {
        perror("sem_unlink(SEM_FULL_NAME) failed");
    }
}

int main(int argc, char *argv[]) {
    // 检查参数
    if (argc != 2) {
        fprintf(stderr, "用法: %s <producer|consumer|cleanup>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    
    // 根据参数执行相应操作
    if (strcmp(argv[1], "producer") == 0) {
        producer_process();
    } 
    else if (strcmp(argv[1], "consumer") == 0) {
        consumer_process();
    } 
    else if (strcmp(argv[1], "cleanup") == 0) {
        global_cleanup();
        printf("已清理所有系统资源\n");
    } 
    else {
        fprintf(stderr, "无效参数: %s\n", argv[1]);
        exit(EXIT_FAILURE);
    }
    
    return 0;
}
